
// 模块插入位置：放在指定模块之后（可自定义）
const INSERT_AFTER_MODULE = 'UserModule';
// 模块输出根目录（固定为 src/modules）
const OUTPUT_DIR = 'src/modules';

const fs = require('fs');
const path = require('path');
const readline = require('readline');
// AppModule 路径配置
const APP_MODULE_PATH = path.join(__dirname, 'src', 'app.module.ts');

// 命名转换工具（驼峰/帕斯卡/短横线命名）
const nameConvert = {
    // 小驼峰：user-info → userInfo
    camel: (str) => {
        return str.replace(/[_-]([a-z])/g, (_, letter) => letter.toUpperCase())
            .replace(/^[A-Z]/, (letter) => letter.toLowerCase());
    },
    // 大驼峰（帕斯卡）：user-info → UserInfo
    pascal: (str) => {
        const camelStr = str.replace(/[_-]([a-z])/g, (_, letter) => letter.toUpperCase());
        return camelStr.charAt(0).toUpperCase() + camelStr.slice(1);
    },
    // 短横线：UserInfo → user-info
    kebab: (str) => {
        return str.replace(/[A-Z]/g, (letter, index) =>
            index === 0 ? letter.toLowerCase() : `-${letter.toLowerCase()}`
        ).replace(/_/g, '-');
    },
};

// 确保目录存在（递归创建）
function ensureDirectoryExistence(dirPath) {
    if (!fs.existsSync(dirPath)) {
        fs.mkdirSync(dirPath, { recursive: true });
        console.log(`📂 创建模块目录：${dirPath}`);
    }
}

// 控制台提问工具（返回Promise处理异步输入）
function createQuestion(rl, question) {
    return new Promise((resolve) => {
        rl.question(question, (answer) => {
            resolve(answer.trim()); // 去除输入前后空格
        });
    });
}

/**
 * 生成 Service/Controller/Module 核心文件
 * @param {Object} config - 生成配置
 * @param {string} config.finalModuleDir - 模块最终目录
 * @param {string} config.kebabName - 短横线命名（如 user-info）
 * @param {string} config.baseClassName - 基础类名（如 UserInfo）
 * @param {string} config.camelName - 小驼峰命名（如 userInfo）
 * @param {string} config.chineseName - 中文名称（如 用户信息）
 * @param {string} config.moduleSubDir - 模块路径（如 system/user-info）
 * @param {string} config.newModuleName - 模块类名（如 UserInfoModule）
 * @param {string} config.newModuleImportStmt - 模块导入语句
 */
function generateCrudFiles(config) {
    const {
        finalModuleDir,
        kebabName,
        baseClassName,
        camelName,
        chineseName,
        moduleSubDir,
        newModuleName,
        newModuleImportStmt,
    } = config;

    console.log(`\n🔧 开始生成 [${baseClassName}]（${chineseName}）三层代码...`);
    ensureDirectoryExistence(finalModuleDir);

    // 1. 生成 Controller（控制器）
    const controllerContent = `import { Controller, Get, Post, Body, Patch, Param, Delete, Query } from "@nestjs/common";
import { ${baseClassName}Service } from "./${kebabName}.service";
import { ApiBearerAuth, ApiOperation, ApiTags } from "@nestjs/swagger";

/**
 * 提示：请根据业务需求完善 DTO 字段
 * 如需字段校验，可导入 class-validator（如 @IsString()、@IsNumber()）
 */
export class Create${baseClassName}Dto {}
export class Update${baseClassName}Dto extends Create${baseClassName}Dto {}

@Controller("${moduleSubDir}") // 控制器路由（与模块路径一致）
@ApiTags("${chineseName}管理")
@ApiBearerAuth() // 如需开放接口，可移除该装饰器或添加 @AllowAnon()
export class ${baseClassName}Controller {
  constructor(private readonly ${camelName}Service: ${baseClassName}Service) {}

  @Post()
  @ApiOperation({ summary: "创建${chineseName}" })
  create(@Body() createDto: Create${baseClassName}Dto) {
    return this.${camelName}Service.create(createDto);
  }

  @Get()
  @ApiOperation({ summary: "查询${chineseName}列表（分页）" })
  findAll(@Query() query: { page?: number; size?: number; [key: string]: any }) {
    return this.${camelName}Service.findAll(query);
  }

  @Get(":id")
  @ApiOperation({ summary: "查询${chineseName}详情" })
  findOne(@Param("id") id: string) {
    return this.${camelName}Service.findOne(+id);
  }

  @Patch(":id")
  @ApiOperation({ summary: "更新${chineseName}" })
  update(@Param("id") id: string, @Body() updateDto: Update${baseClassName}Dto) {
    return this.${camelName}Service.update(+id, updateDto);
  }

  @Delete("batch")
  @ApiOperation({ summary: "批量删除${chineseName}" })
  removeBatch(@Body() { ids }: { ids: number[] }) {
    return this.${camelName}Service.removeBatch(ids);
  }

  @Delete(":id")
  @ApiOperation({ summary: "删除${chineseName}" })
  remove(@Param("id") id: string) {
    return this.${camelName}Service.remove(+id);
  }
}`;

    // 2. 生成 Service（服务层）
    const serviceContent = `import { Injectable, NotFoundException } from "@nestjs/common";
import { ResultData } from "@/common/utils/result";
import { instanceToPlain } from "class-transformer";
import { Create${baseClassName}Dto, Update${baseClassName}Dto } from "./${kebabName}.controller";

@Injectable()
export class ${baseClassName}Service {
  /**
   * 提示：
   * 1. 如需数据库操作：需先定义 Entity 并注入 Repository
   *    示例：constructor(@InjectRepository(${baseClassName}Entity) private repo: Repository<${baseClassName}Entity>) {}
   * 2. 如需调用外部服务：可在此处注入其他 Service
   */
  constructor() {}

  /**
   * 创建${chineseName}
   * @param createDto 创建参数
   */
  async create(createDto: Create${baseClassName}Dto) {
    // TODO: 替换为实际业务逻辑（如数据库插入、接口调用）
    const createdData = {
      id: Date.now(), // 模拟自增ID
      createTime: new Date().toISOString(),
      ...createDto,
    };
    return ResultData.ok(instanceToPlain(createdData), \`${chineseName}创建成功\`);
  }

  /**
   * 分页查询${chineseName}列表
   * @param query 分页参数 + 筛选条件
   */
  async findAll(query: { page?: number; size?: number; [key: string]: any }) {
    const { page = 1, size = 15 } = query;
    const take = +size;
    const skip = (+page - 1) * take;

    // TODO: 替换为实际分页查询逻辑（如 TypeORM findAndCount）
    const mockList = Array.from({ length: take }, (_, i) => ({
      id: skip + i + 1,
      name: \`${chineseName}\${skip + i + 1}\`,
      createTime: new Date().toISOString(),
      ...query,
    }));
    const total = 100; // 模拟总条数

    return ResultData.ok({
      row: instanceToPlain(mockList),
      total,
      page: +page,
      size: take,
      pages: Math.ceil(total / take),
    });
  }

  /**
   * 查询${chineseName}详情
   * @param id 数据ID
   */
  async findOne(id: number) {
    // TODO: 替换为实际单条查询逻辑
    const mockData = { id, name: \`${chineseName}\${id}\`, createTime: new Date().toISOString() };
    
    if (!mockData) {
      throw new NotFoundException(\`${chineseName}（ID：\${id}）不存在\`);
    }
    return ResultData.ok(instanceToPlain(mockData));
  }

  /**
   * 更新${chineseName}
   * @param id 数据ID
   * @param updateDto 更新参数
   */
  async update(id: number, updateDto: Update${baseClassName}Dto) {
    // TODO: 替换为实际更新逻辑（先查后更/直接更新）
    const exists = true; // 实际需查询数据库确认
    if (!exists) {
      throw new NotFoundException(\`${chineseName}（ID：\${id}）不存在\`);
    }
    return ResultData.ok(null, \`${chineseName}更新成功\`);
  }

  /**
   * 批量删除${chineseName}
   * @param ids 数据ID数组
   */
  async removeBatch(ids: number[]) {
    if (ids.length === 0) return ResultData.ok(null, "无数据需删除");
    
    // TODO: 替换为实际批量删除逻辑
    const deletedCount = ids.length;
    return ResultData.ok(null, \`成功删除\${deletedCount}条${chineseName}数据\`);
  }

  /**
   * 单条删除${chineseName}
   * @param id 数据ID
   */
  async remove(id: number) {
    // TODO: 替换为实际删除逻辑
    const exists = true; // 实际需查询数据库确认
    if (!exists) {
      throw new NotFoundException(\`${chineseName}（ID：\${id}）不存在\`);
    }
    return ResultData.ok(null, \`${chineseName}删除成功\`);
  }
}`;

    // 3. 生成 Module（模块配置）
    const moduleContent = `import { Module } from "@nestjs/common";
// 提示：如需数据库支持，取消以下注释并配置 Entity
// import { TypeOrmModule } from "@nestjs/typeorm";
// import { ${baseClassName}Entity } from "./entities/${kebabName}.entity";

import { ${baseClassName}Controller } from "./${kebabName}.controller";
import { ${baseClassName}Service } from "./${kebabName}.service";

@Module({
  imports: [
    // 数据库配置示例（需先定义 Entity）
    // TypeOrmModule.forFeature([${baseClassName}Entity])
  ],
  controllers: [${baseClassName}Controller],
  providers: [${baseClassName}Service],
  exports: [${baseClassName}Service], // 如需对外提供服务，保留此配置
})
export class ${baseClassName}Module {}`;

    // 写入文件
    const filePaths = [
        { path: path.join(finalModuleDir, `${kebabName}.controller.ts`), content: controllerContent },
        { path: path.join(finalModuleDir, `${kebabName}.service.ts`), content: serviceContent },
        { path: path.join(finalModuleDir, `${kebabName}.module.ts`), content: moduleContent },
    ];

    filePaths.forEach(({ path: filePath, content }) => {
        const isExists = fs.existsSync(filePath);
        fs.writeFileSync(filePath, content, 'utf8');
        console.log(`${isExists ? '✅ 更新' : '✅ 生成'}：${path.relative(__dirname, filePath)}`);
    });

    // 4. 将模块添加到 AppModule
    addModuleToAppModule({ newModuleName, newModuleImportStmt });
}

/**
 * 将模块添加到 AppModule 的 imports 数组
 * @param {Object} config - 模块配置
 * @param {string} config.newModuleName - 模块类名（如 UserInfoModule）
 * @param {string} config.newModuleImportStmt - 模块导入语句
 */
function addModuleToAppModule(config) {
    const { newModuleName, newModuleImportStmt } = config;

    try {
        if (!fs.existsSync(APP_MODULE_PATH)) {
            throw new Error(`AppModule 不存在：${APP_MODULE_PATH}`);
        }

        let appModuleContent = fs.readFileSync(APP_MODULE_PATH, 'utf8');

        // 清理重复配置（避免多次运行重复添加）
        appModuleContent = appModuleContent
            .replace(new RegExp(`\\n?\\s*${newModuleImportStmt}\\n?`, 'g'), '')
            .replace(new RegExp(`\\n?\\s*${newModuleName}\\s*,?\\n?`, 'g'), '');

        // 检查是否已配置
        if (appModuleContent.includes(newModuleImportStmt) &&
            (appModuleContent.includes(`, ${newModuleName}`) || appModuleContent.includes(`[${newModuleName}`))) {
            console.log(`✅ AppModule 已存在 ${newModuleName}，跳过添加`);
            return;
        }

        // 1. 插入导入语句（放在 @Global() 前或最后一个 import 后）
        const globalMatch = appModuleContent.match(/@Global\(\)/);
        if (globalMatch) {
            appModuleContent = appModuleContent.slice(0, globalMatch.index) +
                `${newModuleImportStmt}\n\n` +
                appModuleContent.slice(globalMatch.index);
        } else {
            const lastImportMatch = [...appModuleContent.matchAll(/(import\s+{[\s\S]*?}\s+from\s+["'].+?["'];\s*)/g)];
            if (lastImportMatch.length > 0) {
                const lastImport = lastImportMatch.at(-1);
                appModuleContent = appModuleContent.slice(0, lastImport.index + lastImport[0].length) +
                    `\n${newModuleImportStmt}` +
                    appModuleContent.slice(lastImport.index + lastImport[0].length);
            } else {
                appModuleContent = `${newModuleImportStmt}\n\n${appModuleContent}`;
            }
        }

        // 2. 插入模块注册（放在 INSERT_AFTER_MODULE 后或 imports 末尾）
        const moduleMatch = appModuleContent.match(new RegExp(`${INSERT_AFTER_MODULE},\\s*`));
        if (moduleMatch) {
            appModuleContent = appModuleContent.slice(0, moduleMatch.index + moduleMatch[0].length) +
                `${newModuleName},\n        ` +
                appModuleContent.slice(moduleMatch.index + moduleMatch[0].length);
        } else {
            const importsEndMatch = appModuleContent.match(/(\s+)],/);
            if (importsEndMatch) {
                const [fullMatch, indent] = importsEndMatch;
                appModuleContent = appModuleContent.replace(
                    fullMatch,
                    `${indent}    ${newModuleName},\n${fullMatch}`
                );
            } else {
                throw new Error(`未找到 AppModule 的 imports 数组`);
            }
        }

        fs.writeFileSync(APP_MODULE_PATH, appModuleContent, 'utf8');
        console.log(`✅ 已将 ${newModuleName} 加入 AppModule`);
    } catch (error) {
        console.error(`\n⚠️  自动配置 AppModule 失败：${error.message}`);
        console.error(`   请手动添加到 ${APP_MODULE_PATH}：`);
        console.error(`   1. 导入语句：${newModuleImportStmt}`);
        console.error(`   2. 模块注册：在 imports 数组中添加 ${newModuleName},`);
    }
}

/**
 * 主流程：获取用户输入 → 生成配置 → 生成代码
 */
async function main() {
    // 初始化控制台输入
    const rl = readline.createInterface({
        input: process.stdin,
        output: process.stdout,
        terminal: true,
    });

    try {
        console.log('\n📝  NestJS 三层代码生成工具（仅 Service/Controller/Module）');
        console.log('   =====================================================');

        // 1. 获取用户输入
        const moduleSubDir = await createQuestion(rl, '\x1b[33m1. 请输入模块路径（示例：system/user-info）：\x1b[0m');
        const chineseName = await createQuestion(rl, '\x1b[33m2. 请输入中文名称（示例：用户信息）：\x1b[0m');

        // 关闭输入接口
        rl.close();

        // 2. 输入校验
        if (!moduleSubDir || !chineseName) {
            throw new Error('模块路径和中文名称为必填项！\n示例：模块路径=system/user-info，中文名称=用户信息');
        }

        // 3. 生成命名配置
        const baseNamePart = moduleSubDir.split('/').pop().trim(); // 从路径提取基础名（如 user-info）
        if (!baseNamePart) {
            throw new Error('模块路径格式无效！请确保包含具体模块名（如 system/user-info）');
        }

        const config = {
            moduleSubDir,
            chineseName,
            baseClassName: nameConvert.pascal(baseNamePart), // user-info → UserInfo
            kebabName: nameConvert.kebab(baseNamePart),     // user-info
            camelName: nameConvert.camel(baseNamePart),     // userInfo
            finalModuleDir: path.join(OUTPUT_DIR, moduleSubDir), // 最终输出目录
            newModuleName: `${nameConvert.pascal(baseNamePart)}Module`, // UserInfoModule
            newModuleImportStmt: `import { ${nameConvert.pascal(baseNamePart)}Module } from "@/modules/${moduleSubDir.replace(/\\/g, '/')}/${nameConvert.kebab(baseNamePart)}.module";`
        };

        // 3. 打印配置确认
        console.log(`\n📊  生成配置确认：`);
        console.log(`   - 模块路径：${config.moduleSubDir}`);
        console.log(`   - 中文名称：${config.chineseName}`);
        console.log(`   - 输出目录：${path.relative(__dirname, config.finalModuleDir)}`);
        console.log(`   - 模块类名：${config.newModuleName}`);

        // 4. 生成代码
        await generateCrudFiles(config);

        // 5. 生成完成提示
        console.log(`\n🎉  代码生成完成！`);
        console.log(`💡  后续操作建议：`);
        console.log(`   1. 完善 Controller 中的 DTO 字段定义`);
        console.log(`   2. 替换 Service 中的模拟逻辑为实际业务代码`);
        console.log(`   3. 如需数据库支持，添加 Entity 并配置 TypeOrmModule`);
    } catch (error) {
        console.error(`\n❌  执行失败：${error.message}`);
        process.exit(1);
    }
}

// 启动主流程
main();